package com.twoscreen;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.Map;
import java.util.HashMap;
import java.util.logging.Logger;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;


public class YouTubeAuthTokensServlet extends HttpServlet {
	private static final Logger log = Logger.getLogger("com.twoscreen.YouTubeAuthTokensServlet.java");
	private static final long serialVersionUID = 1L;
	private static final String clientID = "859612904977.apps.googleusercontent.com";
	private static final String developer_key = "VXkRXuV8DekjqsOEeFVGRsAT";
	private static final String callbackURI = "https://two-screen.appspot.com/oauth2callback";
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) {
		String success = null;
		String code = (String) request.getParameter("code");
		String viewerId = (String) request.getParameter("state");
		String error = (String) request.getParameter("error");
		if (code != null && viewerId != null) {
			log.info("YouTube authorisation code received. Code: " +
					code + " viewer id: " + viewerId);
			//Exchange authorisation code for refresh and access tokens
			
			Map<String, String> res = getTokens(code, viewerId, request);
			success = res.get("success");
			
			//Return the token too the page
			if (success.equals("true")) {
				//Redirect to creation page
				try {
					Cookie ac = new Cookie("access", res.get("access"));
					//ac.setSecure(true);
					response.addCookie(ac);
					Cookie ex = new Cookie("expiry", res.get("expiry"));
					//ex.setSecure(true);
					response.addCookie(ex);
					log.info("Sending redirect to producers page with cookie");
					response.sendRedirect("/producers/index.jsp");
				} catch (IOException e) {
					log.warning("Failed to redirect: " + e);
				}
			} else {
				sendError(viewerId, success, response);
			}
		} else {
			sendError(viewerId, error, response);
		}
	}
	
	/**
	 * Sends an error message to the user via the HTTP Response
	 * @param viewerId Viewer ID for logging error
	 * @param message Message to send to the user
	 * @param response The HTTP response to send message to
	 */
	private static void sendError(String viewerId, String message, HttpServletResponse response) {
		log.info("YouTube authorisation code not received. ViewerID: " + viewerId +
				" error received: " + message);
		//Display error message
		try {
			response.setContentType("text/html");
			PrintWriter out = response.getWriter();
			out.println("<!doctype html><html><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">");
			out.println("<head>");
			out.println("<title>Error with request</title>");
			out.println("</head>");
			out.println("<body>");
			out.println("<p>There has been an error authorising your YouTube account: " + message + "</p>");
			out.println("<p>You can return to the homepage <a href=\"/\">here</a></p>");
			out.println("</body>");
			out.println("</html>");
			out.flush();
			out.close();
		} catch (IOException e) {
			log.warning("Failed to get the output stream: " + e);
		}
	}
	
	/**
	 * Contacts the YouTube server to exchange the code received by this servlet
	 * for an access token with expiry and a refresh token
	 * @param code The code passed to from YouTube
	 * @param viewerId The viewer ID to update the tokens to
	 * @param request The HTTP request to send the message through
	 * @return A Map containing the token type as the key and the token as the value
	 */
	@SuppressWarnings("null")
	private static Map<String, String> getTokens(String code, String viewerId, HttpServletRequest request) {
		String tokens = "";
		String success = "true";
		
		//Get the authorisation codes
		log.info("Getting the tokens from Google server");
		String requestURI = "https://accounts.google.com/o/oauth2/token";
		String parameters = "code=" + code +
				"&client_id=" + clientID +
				"&client_secret=" + developer_key +
				"&redirect_uri=" + callbackURI +
				"&grant_type=authorization_code";
		
		//Send post message
	    try {
	        URL url = new URL(requestURI);
	        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
	        connection.setDoOutput(true);
	        connection.setRequestMethod("POST");
	        connection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");

	        OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
	        writer.write(parameters);
	        writer.close();

	        if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
	            // OK
	        	log.info("Connection ok");
	        } else {
	            // Server returned HTTP error code.
	        	log.warning("Connection error" + connection.getResponseCode());
	        }
	        StringBuilder sb = new StringBuilder();
			BufferedReader reader = null;
		    try {
		        reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
		        for (String line; (line = reader.readLine()) != null;) {
		            sb.append(line);
		            log.info("adding: " + line);
		        }
		    } finally {
		        if (reader != null) try { reader.close(); } catch (IOException logOrIgnore) {}
		    }
		    tokens = sb.toString();
		    log.info("Got the tokens from Google servers");
	    } catch (MalformedURLException e) {
	    	log.warning("Malformed URL in Auth token request: " + e);
	    } catch (IOException e) {
	    	log.warning("IO exception in Auth token request: " + e);
	    }
	    
		//Get the codes from the received JSON
	    String access = null;
		int expires = 0;
		Date expiryDate = new Date();
		String refresh = null;
		JsonObject obj = null;
	    
	    if (tokens != null || !tokens.equals("")) {
	    	log.info("Parsing JSON : " + tokens);
			JsonParser parser = new JsonParser();
			try {
				obj = parser.parse(tokens).getAsJsonObject();
				log.info("JSON object is: " + obj);
				access = obj.get("access_token").getAsString();
				expires = obj.get("expires_in").getAsInt();
				refresh = obj.get("refresh_token").getAsString();
			} catch (Exception e) {
				log.warning("Exception caught: " + e.fillInStackTrace());
			}
			
			Date d = new Date();
			log.info("got date: " + d);
			long expiryTime = d.getTime() + (expires * 1000);
			log.info("current time: " + d.getTime() + " setting expiry to: " + expiryTime);
			expiryDate.setTime(expiryTime);
			log.info("set expiry date: " + expiryDate);
			
			log.info("access token: " + access + " expiry: " + expiryDate + " refresh: " + refresh);
			
			TUser.addYtTokens(access, expiryDate, refresh, viewerId);
	    }
		
		
		if (access == null && refresh == null) {
			log.warning("No tokens received attempting error");
			try {
				String error = obj.get("error").getAsString();
				log.warning("Error description is: " + error);
				success = error;				
			} catch (NullPointerException e1) {
				log.warning("No error message received");
				success = "there was a problem linking your account - please try again";
			}
		}
		
		//Prepare the return
		Map<String, String> rtn = new HashMap<String, String>();
		rtn.put("success", success);
		rtn.put("access", access);
		rtn.put("expiry", String.valueOf(expiryDate.getTime()));
		
		return rtn;
		
	}
	
}
